/*
 * Copyright 2013-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.gateway.handler;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebHandler;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;

/**
 * WebHandler that delegates to a chain of {@link GlobalFilter} instances and
 * {@link GatewayFilterFactory} instances then to the target {@link WebHandler}.
 *
 * @author Rossen Stoyanchev
 * @author Spencer Gibb
 * @since 0.1
 */
public class FilteringWebHandler implements WebHandler {

	protected static final Log logger = LogFactory.getLog(FilteringWebHandler.class);

	private final List<GatewayFilter> globalFilters;

	public FilteringWebHandler(List<GlobalFilter> globalFilters) {
		/**
		 * 把全局过滤器，转换成GatewayFilter
		 */
		this.globalFilters = loadFilters(globalFilters);
	}

	/**
	 * 使用适配器模式：把所有的GlobalFilter(全局过滤器)转换成GatewayFilter
	 */
	private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {

		return filters.stream().map(filter -> {
			/**
			 * 把GlobalFilter包装成GatewayFilter，最终委托给GlobalFilter执行
			 * GatewayFilterAdapter实现了GatewayFilter，使用组合方式。
			 */
			GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
			if (filter instanceof Ordered) {
				int order = ((Ordered) filter).getOrder();
				return new OrderedGatewayFilter(gatewayFilter, order);
			}
			return gatewayFilter;
		}).collect(Collectors.toList());
	}

	/*
	 * TODO: relocate @EventListener(RefreshRoutesEvent.class) void handleRefresh() {
	 * this.combinedFiltersForRoute.clear();
	 */

	@Override
	public Mono<Void> handle(ServerWebExchange exchange) {
		/**
		 *   1.获取路由对象
		 */
		Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
		/**
		 * 	2.根据路由对象，获取路由对象的GatewayFilter(网关过滤器)
		 */
		List<GatewayFilter> gatewayFilters = route.getFilters();

		/**
		 * 3.把网关过滤器和全局过滤器全部放到一个集合中。
		 */
		List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
		combined.addAll(gatewayFilters);

		/**
		 * 在过滤器的集合中，网关过滤从路由对象中拿到的，所以GatewayFilter只针对于当前路由有效。而全局过滤器对所有的路由都有效。
		 */

		// TODO: needed or cached?
		// 对所有过滤器按照顺序进行排序。
		AnnotationAwareOrderComparator.sort(combined);

//		if (logger.isDebugEnabled()) {
//			logger.debug("Sorted gatewayFilterFactories: " + combined);
//		}

		/**
		 * <core>核心功能：创建默认的网关过滤器链，并调用过滤链的filter方法</core>
		 */
		return new DefaultGatewayFilterChain(combined).filter(exchange);
	}

	private static class DefaultGatewayFilterChain implements GatewayFilterChain {

		/**
		 * 从GatewayFilter集合的第几个索引开始执行。
		 */
		private final int index;

		/**
		 * 接收所有的过滤器集合
		 */
		private final List<GatewayFilter> filters;

		DefaultGatewayFilterChain(List<GatewayFilter> filters) {
			this.filters = filters;
			this.index = 0;
		}

		private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {
			this.filters = parent.getFilters();
			this.index = index;
		}

		public List<GatewayFilter> getFilters() {
			return filters;
		}

		@Override
		public Mono<Void> filter(ServerWebExchange exchange) {
			return Mono.defer(() -> {
				/**
				 * index表示的是过滤器集合的索引值。
				 */
				if (this.index < filters.size()) {
					/**
					 * 获取一个Gateway过滤器
					 */
					GatewayFilter filter = filters.get(this.index);

					DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);
					/**
					 * 调用过滤器链对象的filter
					 */
					return filter.filter(exchange, chain);
				} else {
					return Mono.empty(); // complete
				}
			});
		}

	}

	private static class GatewayFilterAdapter implements GatewayFilter {

		/**
		 * 委托模式
		 */
		private final GlobalFilter delegate;

		GatewayFilterAdapter(GlobalFilter delegate) {
			this.delegate = delegate;
		}

		@Override
		public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
			return this.delegate.filter(exchange, chain);
		}

		@Override
		public String toString() {
			final StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");
			sb.append("delegate=").append(delegate);
			sb.append('}');
			return sb.toString();
		}

	}

}
